<?php

namespace plugin\cms\app\bll;

use app\bll\Site;
use BlueM\Tree;
use BlueM\Tree\Serializer\HierarchicalTreeJsonSerializer;
use core\attribute\Template;
use Exception;
use hulang\tool\FileHelper;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use plugin\cms\app\enum\ContentStatus;
use plugin\cms\app\validate\Category as ValidateCategory;
use support\Db;
use Throwable;

/**
 * 分类
 */
class Category
{


    /**
     * 查找栏目信息
     *
     * @param integer $id
     * @return array
     */
    #[Template]
    public function find(int $id, ?int $siteid = null): array
    {
        $table = Db::table('cms_category')
            ->where('id', '=', $id)
            ->when($siteid != null, function ($query) use ($siteid) {
                return $query->where('siteid', '=', $siteid);
            })
            ->first();
        return $table === null ? [] : get_object_vars($table);
    }

    /**
     * 查询分类
     *
     * @param integer $siteid
     * @param integer|null $pid
     * @return array
     */
    #[Template]
    public function select(int $siteid, ?int $pid = null): array
    {

        return  Db::table('cms_category')
            ->leftJoin('cms_model', 'cms_category.mid', '=', 'cms_model.id')
            ->where('cms_category.siteid', '=', $siteid)
            ->when($pid !== null, function ($query) use ($pid) {
                return $query->where('pid', '=', $pid);
            })
            ->orderByRaw(' sort desc')
            ->get(['cms_category.*', 'cms_model.title as modeltitle', 'cms_model.id as mid',  'cms_model.tablename as tablename'])
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
    }





    /**
     * 添加栏目
     *
     * @param array $data
     * @return integer
     */
    public function add(array $data): int
    {

        $ValidateCategory = new ValidateCategory();
        if (!$ValidateCategory->check($data)) {
            throw new Exception($ValidateCategory->getSimpleError());
        }
        $data['content'] = htmlspecialchars_decode($data['content']);

        return   Db::table('cms_category')->insertGetId($data);
    }


    /**
     * 编辑
     *
     * @param integer $id
     * @param integer $siteid
     * @param array $data
     * @return integer
     */
    public function edit(int $id, int $siteid, array $data): int
    {
        $ValidateCategory = new ValidateCategory();
        if (!$ValidateCategory->check($data)) {
            throw new Exception($ValidateCategory->getSimpleError());
        }
        $data['content'] = htmlspecialchars_decode($data['content']);
        return   Db::table('cms_category')
            ->where('id', '=', $id)
            ->where('siteid', '=', $siteid)
            ->update($data);
    }

    /**
     * 删除
     *
     * @param integer $id
     * @param integer $siteid
     * @return void
     */
    public function delete(int $id, int $siteid)
    {

        Db::beginTransaction();
        try {

            $childs = $this->select(siteid: $siteid, pid: $id);
            if ($childs) {
                throw new Exception('请先删除子类');
            }
            $cateinfo  = $this->find($id, $siteid);
            $bllModel  = new Model();
            $modelinfo = $bllModel->find($cateinfo['mid'], $siteid);

            //删除内容
            Db::table($modelinfo['tablename'])
                ->where('id', '=', $id)
                ->where('siteid', '=', $siteid)
                ->delete();

            //删除内容标签
            Db::table('cms_tag_content')
                ->where('cid', '=', $id)
                ->where('siteid', '=', $siteid)
                ->delete();

            //删除栏目
            Db::table('cms_category')
                ->where('id', '=', $id)
                ->where('siteid', '=', $siteid)
                ->delete();
            Db::commit();
        } catch (Throwable $ex) {
            Db::rollBack();
        }
    }

    /**
     * 栏目排序
     *
     * @param integer $id
     * @param float $sort
     * @param integer $siteid
     * @return integer
     */
    public function sort(int $id, float $sort, int $siteid): int
    {
        return   Db::table('cms_category')
            ->where('id', '=', $id)
            ->where('siteid', '=', $siteid)
            ->update([
                'sort' => $sort
            ]);
    }


    /**
     * 栏目层级结构
     *
     * @param integer $siteid
     * @param boolean $tojson
     * @param string $title
     * @return string|array|Tree
     */
    public function cascader(int $siteid, bool $tojson = true, string $subtitle = ''): string|array|Tree
    {
        $data       = $this->select($siteid);

        foreach ($data as $key => $d) {
            $data[$key]['spread'] = true;
            $data[$key]['title'] = $d['title'] . ($subtitle ? ' (' . $d[$subtitle] . ') ' : '');
        };

        $tree = new Tree(
            $data,
            [
                'rootId'         => 0,
                'id'             => 'id',
                'parent'         => 'pid',
                'jsonSerializer' => new HierarchicalTreeJsonSerializer()
            ]
        );

        return $tojson ? json_encode($tree, JSON_UNESCAPED_UNICODE) : $tree;
    }

    /**
     * 层级关系 只允许某些模型可选
     *
     * @param integer $siteid
     * @param array $allowmids
     * @param boolean $tojson
     * @return string|array|Tree
     */
    public function cascaderAllowMid(
        int $siteid,
        array $allowmids,
        bool $tojson = true
    ): string|array|Tree {
        $data = $this->select($siteid);

        foreach ($data as $key => $d) {
            if (!in_array($d['mid'], $allowmids)) {
                $data[$key]['disabled'] = true;
            }
            $data[$key]['spread'] = true;
        };

        $tree = new Tree(
            $data,
            [
                'rootId'         => 0,
                'id'             => 'id',
                'parent'         => 'pid',
                'jsonSerializer' => new HierarchicalTreeJsonSerializer()
            ]
        );

        return $tojson ? json_encode($tree, JSON_UNESCAPED_UNICODE) : $tree;
    }



    /**
     * 获取栏目的表名
     *
     * @param integer $id 栏目ID
     * @param integer $siteid
     * @return string
     */
    public function getTableName(int $id, ?int $siteid = null): string
    {
        $info = $this->find($id, $siteid);
        if (!$info) {
            return '';
        }

        $bllModel = new Model();
        $modelinfo = $bllModel->find($info['mid'], $siteid);
        if (!$modelinfo) {
            return '';
        }

        return $modelinfo['tablename'];
    }

    /**
     * 可用的栏目模板
     *
     * @param integer $cid
     * @param integer $siteid
     * @return array
     */
    public function getUsableCateTemp(int $siteid): array
    {

        $files = FileHelper::getFolder($this->getHomeCmsTempPath($siteid));
        if (!$files) {
            return [];
        }
        $temps = [];

        foreach ($files as $file) {
            if (
                str_starts_with($file['name'], 'cate_')
                ||
                $file['name'] == 'cate.php'
            ) {
                $temps[] = $file['name'];
            }
        }

        return  $temps;
    }

    /**
     *可以页面模板
     *
     * @param integer $siteid
     * @return array
     */
    public function getUsablePageTemp(int $siteid): array
    {
        $files = FileHelper::getFolder($this->getHomeCmsTempPath($siteid));
        if (!$files) {
            return [];
        }
        $temps = [];
        foreach ($files as $file) {
            if (
                str_starts_with($file['name'], 'page_')
                ||
                $file['name'] == 'page.php'
            ) {
                $temps[] = $file['name'];
            }
        }

        return  $temps;
    }


    /**
     * CMS前台模板路径
     *
     * @param integer $siteid
     * @return string
     */
    private function getHomeCmsTempPath(int $siteid): string
    {
        //获取站点信息
        $bllSite  = new Site();
        $siteinfo = $bllSite->find($siteid);
        $siteinfo['theme'];

        $theme = $siteinfo['theme'] == '' ? '' : $siteinfo['theme'];
        return $templatepath = base_path() .
            '/template/home/' .
            $siteinfo['ident'] .
            '/' .
            ($theme == '' ? '' : $theme . '/')
            . 'cms/Index';
    }


    /**
     * 根据数据解析Tree 
     *
     * @param array $data
     * @return Tree
     */
    public function cateTree(array $data): Tree
    {
        return new Tree(
            $data,
            [
                'rootId'         => 0,
                'id'             => 'id',
                'parent'         => 'pid'
            ]
        );
    }

    /**
     * 获取自己和自己的父级
     *
     * @param integer $id
     * @param integer $siteid
     * @return array
     */
    #[Template]
    public function getAncestorsAndSelf(int $id, int $siteid): array
    {
        $data = $this->select($siteid);
        $tree = $this->cateTree($data);

        $node              = $tree->getNodeById($id);
        $ancestorsPlusSelf = $node->getAncestorsAndSelf();
        $parents           = [];
        foreach ($ancestorsPlusSelf as $ancestor) {
            $parents[] = $ancestor->toArray();
        }
        krsort($parents);
        return array_values($parents);
    }


    /**
     * 获取兄弟姐妹 同级栏目
     *
     * @param integer $id
     * @param integer $siteid
     * @return array
     */
    #[Template]
    public function getSiblingsAndSelf(int $id, int $siteid): array
    {
        $data = $this->select($siteid);
        $tree = $this->cateTree($data);

        $node  = $tree->getNodeById($id);
        $siblings = $node->getSiblingsAndSelf();
        $result = [];
        foreach ($siblings as $sibling) {
            $result[] = $sibling->toArray();
        }
        return $result;
    }


    /**
     * 获取父级
     *
     * @param integer $id
     * @param integer $siteid
     * @return array
     */
    #[Template]
    public function getAncestors(int $id, int $siteid): array
    {
        $data = $this->select($siteid);
        $tree = $this->cateTree($data);

        $node              = $tree->getNodeById($id);
        $ancestors = $node->getAncestors();
        $parents           = [];
        foreach ($ancestors as $ancestors) {
            $parents[] = $ancestors->toArray();
        }
        krsort($parents);
        return array_values($parents);
    }


    /**
     * 获取子集
     *
     * @param integer $id
     * @param integer $siteid
     * @return array
     */
    #[Template]
    public function getDescendants(int $id, int $siteid): array
    {
        $data = $this->select($siteid);
        $tree = $this->cateTree($data);

        $node        = $tree->getNodeById($id);
        $descendants = $node->getDescendants();
        $result = [];
        foreach ($descendants as $descendant) {
            $result[] = $descendant->toArray();
        }
        return $result;
    }

    /**
     * 获取子集 包含自己
     *
     * @param integer $id
     * @param integer $siteid
     * @return array
     */
    #[Template]
    public function getDescendantsAndSelf(int $id, int $siteid): array
    {
        $data = $this->select($siteid);
        $tree = $this->cateTree($data);

        $node        = $tree->getNodeById($id);
        $descendants = $node->getDescendantsAndSelf();
        $result = [];
        foreach ($descendants as $descendant) {
            $result[] = $descendant->toArray();
        }
        return $result;
    }


    /**
     * 智能获取子集 没有子集则获取兄弟姐妹
     *
     * @param integer $id
     * @param integer $siteid
     * @return array
     */
    #[Template]
    public function getDescendantsSiblings(int $id, int $siteid): array
    {
        $descendants = $this->getDescendants($id, $siteid);
        if ($descendants) {
            return $descendants;
        }

        return $this->getSiblingsAndSelf($id, $siteid);
    }

    /**
     * 设置是否可以评论
     *
     * @param integer $id
     * @param integer $comment
     * @param integer $siteid
     * @return integer
     */
    public function setComment(int $id, int $comment, int $siteid): int
    {
        return Db::table('cms_category')
            ->where('id', '=', $id)
            ->where('siteid', '=', $siteid)
            ->update([
                'comment' => $comment
            ]);
    }

    /**
     * 设置是否可以点赞
     *
     * @param integer $id
     * @param integer $praise
     * @param integer $siteid
     * @return integer
     */
    public function setPraise(int $id, int $praise, int $siteid): int
    {
        return Db::table('cms_category')
            ->where('id', '=', $id)
            ->where('siteid', '=', $siteid)
            ->update([
                'praise' => $praise
            ]);
    }

    /**
     * 设置是否可以收藏
     *
     * @param integer $id
     * @param integer $collect
     * @param integer $siteid
     * @return integer
     */
    public function setCollect(int $id, int $collect, int $siteid): int
    {
        return Db::table('cms_category')
            ->where('id', '=', $id)
            ->where('siteid', '=', $siteid)
            ->update([
                'collect' => $collect
            ]);
    }

    /**
     * 设置是否可以投稿
     *
     * @param integer $id
     * @param integer $contribute
     * @param integer $siteid
     * @return integer
     */
    public function setContribute(int $id, int $contribute, int $siteid): int
    {
        return Db::table('cms_category')
            ->where('id', '=', $id)
            ->where('siteid', '=', $siteid)
            ->update([
                'contribute' => $contribute
            ]);
    }


    /**
     * 可以投稿的栏目层级展示
     *
     * @return array
     */
    public function contributeCascader(): array
    {
        $cates = Db::table('cms_category')
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();


        $sitecates = [];
        foreach ($cates as $cate) {
            $sitecates[$cate['siteid']][] = $cate;
        }

        //查询可投稿的站点        
        $siteids = array_keys($sitecates);
        $bllSite = new Site();
        $sites = $bllSite->selectInIds($siteids);
        $sites = array_column($sites, null, 'id'); //ID做键值


        $parseContributeCate = [];
        foreach ($sitecates as $sitekey => $sitecate) {

            $contributionCate = $this->_contributeCascader($sitecate, 0);
            foreach ($contributionCate as $key => $cate) {
                if (!isset($cate['children']) && $cate['contribute'] == 0) {
                    unset($contributionCate[$key]);
                }
            }
            if (!$contributionCate) {
                continue;
            }
            $parseContributeCate[] = [
                'id' => $sites[$sitekey]['title'],
                'title' => $sites[$sitekey]['title'],
                'disabled' => true,
                'children' => array_values($contributionCate),
            ];
        }
        if (count($parseContributeCate) == 1) {
            return $parseContributeCate[0]['children'];
        }

        return $parseContributeCate;
    }

    private function _contributeCascader($cates, $pid = 0)
    {
        $parsecates = [];
        foreach ($cates as $key => $cate) {

            if ($cate['contribute'] == 0) {
                $cate['disabled'] = true;
            }
            if ($pid == $cate['pid']) {

                $child = $this->_contributeCascader($cates, $cate['id']);
                foreach ($child as $k => $c) {
                    if ($c['contribute'] == 0 && !isset($c['children'])) {
                        unset($child[$k]);
                    }
                }

                if ($child) {
                    $cate['children'] = array_values($child);
                }
                $parsecates[] = $cate;
            }
        }
        return $parsecates;
    }

    /**
     * 计算栏目下的内容待审数量
     *
     * @param integer $siteid
     * @param integer $status
     * @return void
     */
    public function computeContentPendingCount(int $siteid)
    {

        $cates = $this->select(siteid: $siteid);
        $bllContent = new Content();
        foreach ($cates as $cate) {
            $count = $bllContent->contentCount(modelTable: $cate['tablename'], cid: $cate['id'], status: ContentStatus::STATUS_PENDING->value, siteid: $siteid);

            Db::table('cms_category')
                ->where('id', '=', $cate['id'])
                ->where('siteid', '=', $siteid)
                ->update([
                    'pendingcount' => $count
                ]);
        }
    }


    /**
     * 计算栏目下的内容已发布数量
     *
     * @param integer $siteid
     * @param integer $status
     * @return void
     */
    public function computeContentPublishedCount(int $siteid)
    {

        $cates = $this->select(siteid: $siteid);
        $bllContent = new Content();

        foreach ($cates as $cate) {
            $count = $bllContent->contentCount(modelTable: $cate['tablename'], cid: $cate['id'], status: ContentStatus::STATUS_NORMAL->value, siteid: $siteid);
            Db::table('cms_category')
                ->where('id', '=', $cate['id'])
                ->where('siteid', '=', $siteid)
                ->update([
                    'publishedcount' => $count
                ]);
        }
    }


    /**
     * 计算栏目下的回收站内容数量
     *
     * @param integer $siteid
     * @param integer $status
     * @return void
     */
    public function computeContentRecycleCount(int $siteid)
    {

        $cates = $this->select(siteid: $siteid);
        $bllContent = new Content();

        foreach ($cates as $cate) {
            $count = $bllContent->contentCount(modelTable: $cate['tablename'], cid: $cate['id'], status: ContentStatus::STATUS_RECYCLE->value, siteid: $siteid);
            Db::table('cms_category')
                ->where('id', '=', $cate['id'])
                ->where('siteid', '=', $siteid)
                ->update([
                    'recyclecount' => $count
                ]);
        }
    }

    /**
     * 更新某栏目下的文章数
     *
     * @param integer $cid
     * @param integer $status
     * @param integer|null $siteid
     * @return void
     */
    public  function computeContentCountAtCid(int $cid,  ?int $siteid = null)
    {
        $cate = $this->find($cid, $siteid);
        if ($cate) {
            $bllContent = new Content();
            $tablename = $this->getTableName($cid, $siteid);

            $status = [

                ContentStatus::STATUS_PENDING->value => 'pendingcount',
                ContentStatus::STATUS_NORMAL->value => 'publishedcount',
                ContentStatus::STATUS_RECYCLE->value => 'recyclecount',

            ];

            foreach ($status as $statuskey => $value) {
                $count = $bllContent->contentCount(modelTable: $tablename, cid: $cid, status: $statuskey, siteid: $siteid);
                Db::table('cms_category')
                    ->where('id', '=', $cate['id'])
                    ->where('siteid', '=', $siteid)
                    ->update([
                        $value => $count
                    ]);
            }
        }
    }
}
